/*
 * BlockDesc.cpp
 *
 *  Created on: Mar 23, 2010
 *      Author: Lukasz Spintzyk
 */

#include "BlockGroup.h"
#include "IOStream.h"
#include "Inode.h"
#include "fs_config.h"


//uint32_t BlockDesc::s_blockSize = 1024;
//uint32_t BlockDesc::s_blocksPerGroup = s_blockSize*8;

BlockDesc::BlockDesc(uint32_t nb)
{
	setGroupNumber(nb);
}

BlockDesc::~BlockDesc()
{
}

void BlockDesc::setSuperBlock(const SuperBlock& sb)
{
    d_sb = sb;
    if (d_blocksBitMap.size() != d_sb.d_blocksPerGroup)
    {
        d_blocksBitMap.resize(d_sb.d_blocksPerGroup, 0); // TODO make sure it is right
    }

    if (d_inodeBitmap.size() != d_sb.d_inodesPerGroup)
    {
        d_inodeBitmap.resize(d_sb.d_inodesPerGroup, 0);
    }

    d_blockBitmapNb = d_groupNumber*d_sb.d_blocksPerGroup
    		+ (d_sb.d_blockSize == 1024) ? 3 : 2;

    d_inodeBitmapNb = d_blockBitmapNb + 1;
    d_inodeTableBlockNb = d_inodeBitmapNb + 1;

    d_gOffset = d_groupNumber*d_sb.d_blockSize*d_sb.d_blocksPerGroup;
}

SuperBlock& BlockDesc::getSuperBock()
{
    return d_sb;
}

void BlockDesc::readDesc(byte* buffer)
{
	d_superBlockModified = true;
    byte* buff = buffer;//d_blocks.get();

	//uint32_t begin = d_gOffset;
	//uint32_t end = d_gOffset + 1024;
	//d_sb.read(buff + 1024); // first 1024 bytes are empty

	buff = buffer + d_sb.d_blockSize * (d_sb.d_blockSize == 1024 ? 2 : 1 );
	//read block desc

	buff += d_sb.d_blockSize;

	d_blocksBitMap.resize(d_sb.d_blocksPerGroup, 0);
	d_inodeBitmap.resize(d_sb.d_inodesPerGroup, 0);
	//std::cout << "Num blocks " << d_blocksBitMap.num_blocks() << " Size " << d_blocksBitMap.size() << std::endl;
	//read d_blocksBitMap
	boost::from_block_range(buff, buff+d_blocksBitMap.num_blocks(), d_blocksBitMap);
	

	buff += d_sb.d_blockSize;
	//read d_inodeBitMap
	boost::from_block_range(buff, buff+d_inodeBitmap.num_blocks(), d_inodeBitmap);
}

bool BlockDesc::writeDesc(byte* buffer)
{

	byte* buff = buffer + 1024;
	
	d_sb.write(buff);

	buff = buffer + d_sb.d_blockSize * ((d_sb.d_blockSize == 1024) ? 2 : 1 );
	// TODO read block desc

	buff += d_sb.d_blockSize;

	//write d_blocksBitMap
	d_blocksBitMap.set(0, true);
	d_blocksBitMap.set(1, true); //superblock
	d_blocksBitMap.set(2, true); //blocksDesc
	d_blocksBitMap.set(3, true); //blocksBitmap
	d_blocksBitMap.set(4, true); //inodeBitmap

	uint32_t inodes_per_block = d_sb.d_blockSize/Inode::size();
	uint32_t inode_blocks_nb = d_sb.d_inodesPerGroup/inodes_per_block;
	for(uint32_t i = 0; i < inode_blocks_nb+sizeInBlocks() ; ++i)
	{
		d_blocksBitMap.set(i, true);
	}

	assert(d_blocksBitMap.size() == d_sb.d_blocksPerGroup);
	boost::to_block_range(d_blocksBitMap, buff);

	buff += d_sb.d_blockSize;
	//write d_inodeBitMap
	assert(d_inodeBitmap.size() == d_sb.d_inodesPerGroup);
	boost::to_block_range(d_inodeBitmap, buff);

	return d_superBlockModified; // TODO if modified superblock than return true;
}

void BlockDesc::updateStatValues()
{
	//todo add tests or asserts
	d_freeBlocks = d_blocksBitMap.size() - d_blocksBitMap.count()
			- d_sb.d_inodesPerGroup/Inode::size();

	d_freeInodes = d_inodeBitmap.size() - d_inodeBitmap.count();

}

void BlockDesc::setGroupNumber(uint32_t number)
{
    d_groupNumber = number;
}

uint32_t BlockDesc::getGroupNumber()
{
	return d_groupNumber;
}

bool BlockDesc::hasBlock(uint32_t nr)
{
	assert(d_blocksBitMap.size() >= nr);
	return d_blocksBitMap.test(nr);
}

bool BlockDesc::hasInode(uint32_t nr)
{
	assert(d_inodeBitmap.size() >= nr);
	return d_inodeBitmap.test(nr-1);
}

void BlockDesc::setInode(uint32_t nr, bool bit)
{
	// TODO assert(bit != d_inodeBitmap.test(nr));
	assert(nr>0);
	d_inodeBitmap.set(nr-1, bit);
	bit ? d_freeInodes-- : d_freeInodes++;
}

uint32_t BlockDesc::getFreeInode()
{
	if (d_inodeBitmap.count() == d_inodeBitmap.size())
	{
		return 0;
	}
	uint32_t size = d_inodeBitmap.size();
	uint32_t bit = (d_groupNumber == 0) ? RESERVED_INODES : 0;
	for(; bit < size; ++bit)
	{
		if(!d_inodeBitmap.test(bit))
		{
			return bit+1;
		}
	}
	return 0;
}

void BlockDesc::getBlock(uint32_t nr, void* buffer)
{
	//assert(nr < d_sb.d_blocksPerGroup);
	//uint32_t begin = d_gOffset + nr*d_sb.d_blockSize;
    //  d_io.readRange(buffer, begin, begin+d_sb.d_blockSize);
}

void BlockDesc::setBlock(uint32_t nr, const void* buffer)
{
	assert(nr < d_sb.d_blocksPerGroup);
	d_blocksBitMap.set(nr, true);
	d_superBlockModified = true;
}

uint32_t BlockDesc::sizeInBlocks()
{
	return ( (d_sb.d_blockSize == 1024) ? 5 : 4 );
}

uint32_t BlockDesc::inodeTableSize()
{
	return (d_sb.d_inodesPerGroup*Inode::size())/d_sb.d_blockSize;
}

bool BlockDesc::tryReserveBlocks(const uint32_t num, uint32_t& retBlock, const uint32_t after_block)
{
	uint32_t size = d_blocksBitMap.size();
	assert(size == d_sb.d_blocksPerGroup);
	// TODO 9 std::cout << "size " << d_blocksBitMap.size() << " count " << d_blocksBitMap.count() << std::endl;
	uint32_t len = 0;
	retBlock = after_block;

	for (uint32_t bit = after_block; (bit < size) && (len < num); ++bit)
	{
		if (d_blocksBitMap.test(bit))
		{
			len=0;
		}
		else
		{
			if (len == 0)
			{
				retBlock = bit;
			}
			len++;
		}
	}

	if (num ==1 && len ==0){
		assert((d_blocksBitMap.size() - d_blocksBitMap.count()) == 0);

	}
	if (len >= num)
	{
		uint32_t bit = retBlock;
		len = num;
		assert(!d_blocksBitMap.test(bit));
		while (len--)
		{
			assert(!d_blocksBitMap.test(bit));
			d_blocksBitMap.set(bit++, true);
		}
		d_superBlockModified = true;
		return true;
	}
	return len >= num;
}

uint32_t BlockDesc::reservedBlocks()
{
	return d_blocksBitMap.count();
}

uint32_t BlockDesc::freeBlocks()
{
	return d_blocksBitMap.size() - d_blocksBitMap.count();
}

